{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3",
      "language": "python"
    },
    "language_info": {
      "name": "python",
      "version": "3.7.6",
      "mimetype": "text/x-python",
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "pygments_lexer": "ipython3",
      "nbconvert_exporter": "python",
      "file_extension": ".py"
    },
    "colab": {
      "name": "34 - Build a QA database",
      "provenance": [],
      "collapsed_sections": []
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "POWZoSJR6XzK"
      },
      "source": [
        "# Build a QA database\n",
        "\n",
        "Conversational AI is a growing field that could potentially automate much of the customer service industry. Full automation is still a ways away (most of us have been on a call with an automated agent and just want to get to a person) but it certainly can be a solid first line before human intervention.\n",
        "\n",
        "This notebook presents a process to answer user questions using a txtai embeddings instance. It's not conversational AI but instead looks to find the closest existing question to a user question. This is useful in cases where there are a list of frequently asked questions. "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qa_PPKVX6XzN"
      },
      "source": [
        "# Install dependencies\n",
        "\n",
        "Install `txtai` and all dependencies."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5",
        "_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19",
        "trusted": true,
        "_kg_hide-output": true,
        "id": "24q-1n5i6XzQ"
      },
      "source": [
        "%%capture\n",
        "!pip install git+https://github.com/neuml/txtai datasets"
      ],
      "execution_count": 75,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Load the dataset\n",
        "\n",
        "We'll use a Hugging Face dataset of web questions for this example. The dataset has a list of questions and answers. The code below loads the dataset and prints a couple examples to get an idea of how the data is formatted."
      ],
      "metadata": {
        "id": "QAEVXfOiVzEB"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from datasets import load_dataset\n",
        "\n",
        "ds = load_dataset(\"web_questions\", split=\"train\")\n",
        "\n",
        "for row in ds.select(range(5)):\n",
        "  print(row[\"question\"], row[\"answers\"])"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "koM4vYHXL82P",
        "outputId": "91926a1b-8b9c-46be-f450-f76a1e3aab82"
      },
      "execution_count": 76,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "what is the name of justin bieber brother? ['Jazmyn Bieber', 'Jaxon Bieber']\n",
            "what character did natalie portman play in star wars? ['Padmé Amidala']\n",
            "what state does selena gomez? ['New York City']\n",
            "what country is the grand bahama island in? ['Bahamas']\n",
            "what kind of money to take to bahamas? ['Bahamian dollar']\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Create index\n",
        "\n",
        "Next, we'll create a txtai index. The question will be the indexed text. We'll also store full content so we can access the answer at query time.\n"
      ],
      "metadata": {
        "id": "0p3WCDniUths"
      }
    },
    {
      "cell_type": "code",
      "metadata": {
        "_uuid": "d629ff2d2480ee46fbb7e2d37f6b5fab8052498a",
        "_cell_guid": "79c7e3d0-c299-4dcb-8224-4455121ee9b0",
        "trusted": true,
        "id": "2j_CFGDR6Xzp"
      },
      "source": [
        "from txtai.embeddings import Embeddings\n",
        "\n",
        "# Create embeddings index with content enabled. The default behavior is to only store indexed vectors.\n",
        "embeddings = Embeddings({\"path\": \"sentence-transformers/nli-mpnet-base-v2\", \"content\": True})\n",
        "\n",
        "# Map question to text and store content\n",
        "embeddings.index([(uid, {\"url\": row[\"url\"], \"text\": row[\"question\"], \"answer\": \", \".join(row[\"answers\"])}, None) for uid, row in enumerate(ds)])"
      ],
      "execution_count": 77,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Asking questions\n",
        "\n",
        "Now that the index is built, let's ask some questions! We'll use txtai SQL to select the fields we want to return.\n",
        "\n",
        "See the list of questions asked and best matching question-answer combo."
      ],
      "metadata": {
        "id": "dqx-PmpzWMez"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "def question(text):\n",
        "  return embeddings.search(f\"select text, answer, score from txtai where similar('{text}') limit 1\")\n",
        "\n",
        "question(\"What is the timezone of NYC?\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Gatp4mDQPA_v",
        "outputId": "93efaec7-c41e-4865-f042-371c55170ffe"
      },
      "execution_count": 78,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[{'answer': 'North American Eastern Time Zone',\n",
              "  'score': 0.8904051184654236,\n",
              "  'text': 'what time zone is new york under?'}]"
            ]
          },
          "metadata": {},
          "execution_count": 78
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "question(\"Things to do in New York\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "WUvdAvvlSPDT",
        "outputId": "00ed87fe-b659-406e-d03a-97b7b5ea9773"
      },
      "execution_count": 79,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[{'answer': \"Chelsea Art Museum, Brooklyn Bridge, Empire State Building, The Broadway Theatre, American Museum of Natural History, Central Park, St. Patrick's Cathedral, Japan Society of New York, FusionArts Museum, American Folk Art Museum\",\n",
              "  'score': 0.8308358192443848,\n",
              "  'text': 'what are some places to visit in new york?'}]"
            ]
          },
          "metadata": {},
          "execution_count": 79
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "question(\"Microsoft founder\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "vvsSj2qKRrrY",
        "outputId": "1c4a94d7-3bf6-4cae-8b40-13fbfef35205"
      },
      "execution_count": 80,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[{'answer': 'Bill Gates',\n",
              "  'score': 0.6617322564125061,\n",
              "  'text': 'who created microsoft windows?'}]"
            ]
          },
          "metadata": {},
          "execution_count": 80
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "question(\"Apple founder university\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "UUD56XSRR1jc",
        "outputId": "fb94e89d-1580-471b-9e0e-541e5208d937"
      },
      "execution_count": 81,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[{'answer': 'Reed College',\n",
              "  'score': 0.5137897729873657,\n",
              "  'text': 'what college did steve jobs attend?'}]"
            ]
          },
          "metadata": {},
          "execution_count": 81
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "question(\"What country uses the Yen?\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "D6Ur0wZWSBfd",
        "outputId": "caf6e0aa-af62-4f33-fed1-6e1a111fed1a"
      },
      "execution_count": 82,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[{'answer': 'Japanese yen',\n",
              "  'score': 0.6663530468940735,\n",
              "  'text': 'what money do japanese use?'}]"
            ]
          },
          "metadata": {},
          "execution_count": 82
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "question(\"Show me a list of Pixar movies\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "2POCUWrqSKGP",
        "outputId": "f8069bf4-a135-47df-a571-198f168c03fb"
      },
      "execution_count": 83,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[{'answer': \"A Bug's Life, Toy Story 2, Ratatouille, Cars, Up, Toy Story, Monsters, Inc., The Incredibles, Finding Nemo, WALL-E\",\n",
              "  'score': 0.653051495552063,\n",
              "  'text': 'what does pixar produce?'}]"
            ]
          },
          "metadata": {},
          "execution_count": 83
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "question(\"What is the timezone of Florida?\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "xll4a1ChTaVg",
        "outputId": "00cb967f-753e-4d15-cb87-308c08dfde59"
      },
      "execution_count": 84,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[{'answer': 'North American Eastern Time Zone',\n",
              "  'score': 0.9672279357910156,\n",
              "  'text': 'where is the time zone in florida?'}]"
            ]
          },
          "metadata": {},
          "execution_count": 84
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "question(\"Tell me an animal found offshore in Florida\")"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "4EyAOhWXUcKe",
        "outputId": "ef3f83b1-9314-4458-843e-16afb3364cd9"
      },
      "execution_count": 85,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "[{'answer': 'Largemouth bass',\n",
              "  'score': 0.6526554822921753,\n",
              "  'text': 'what kind of fish do you catch in florida?'}]"
            ]
          },
          "metadata": {},
          "execution_count": 85
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Not too bad! This database only has over 6,000 question-answer pairs. To improve quality a score filter could be put on the query to only return highly confident answers. But this gives an idea of what is possible."
      ],
      "metadata": {
        "id": "KFxsjtsnWgpe"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Run as an application\n",
        "\n",
        "This can also be run as an application. See below."
      ],
      "metadata": {
        "id": "2x9awoKNZfZg"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from txtai.app import Application\n",
        "\n",
        "# Save index\n",
        "embeddings.save(\"questions.tar.gz\")\n",
        "\n",
        "# Build application and index data\n",
        "app = Application(\"path: questions.tar.gz\")\n",
        "\n",
        "# Run search query\n",
        "app.search(\"select text, answer, score from txtai where similar('Tell me an animal found offshore in Florida') limit 1\")[0]"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "0lH9cf1bZokt",
        "outputId": "77afcb03-c834-46c9-98f2-b1a3c34c0e3b"
      },
      "execution_count": 86,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'answer': 'Largemouth bass',\n",
              " 'score': 0.6526554822921753,\n",
              " 'text': 'what kind of fish do you catch in florida?'}"
            ]
          },
          "metadata": {},
          "execution_count": 86
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aDIF3tYt6X0O"
      },
      "source": [
        "# Wrapping up\n",
        "\n",
        "This notebook introduced a simple question matching service. This could be the foundation of an automated customer service agent and/or an online FAQ.\n",
        "\n",
        "For a full example, see [codequestion](https://github.com/neuml/codequestion), which is an application that matches user questions to Stack Overflow question-answer pairs."
      ]
    }
  ]
}
